package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.manager.VodManager;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.BusinessException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.utils.ThrowUtil;
import com.atguigu.tingshu.enums.TrackStatType;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.model.VodUploadResponse;
import com.tencentcloudapi.vod.v20180717.models.DescribeMediaInfosResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({ "all" })
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;
    @Autowired
    private VodManager vodManager;
    @Autowired
    private VodConstantProperties vodConstantProperties;
    @Autowired
    private AlbumInfoMapper albumInfoMapper;
    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * @param @param multipartFile
     * @return @return {@code Map<String, String> }
     * @name uploadTrack
     * @description 上传声音
     */
    @Override
    public Map<String, String> uploadTrack(MultipartFile multipartFile) {
        // 1. 上传声音
        // 保存本地临时文件
        String originalFilename = multipartFile.getOriginalFilename();
        String filePath = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        String fileName = UUID.randomUUID().toString().replace("-", "")
                + originalFilename.substring(originalFilename.lastIndexOf("."));
        // 创建文件夹
        if (!FileUtil.exist(vodConstantProperties.getTempPath() + filePath)) {
            FileUtil.mkdir(vodConstantProperties.getTempPath() + filePath);
        }
        try {
            multipartFile
                    .transferTo(new File(vodConstantProperties.getTempPath() + filePath + File.separator + fileName));
        } catch (IOException e) {
            log.error("文件保存本地临时目录失败");
            throw new BusinessException(ResultCodeEnum.SERVICE_ERROR, "文件保存本地临时目录失败");
        }
        VodUploadResponse uploadResponse = vodManager.uploadVideo(fileName, filePath);
        Map<String, String> result = new HashMap<>();
        result.put("mediaFileId", uploadResponse.getFileId());
        result.put("mediaUrl", uploadResponse.getMediaUrl());
        return result;
    }

    /**
     * @param @param trackInfoVo
     * @return
     * @name saveTrackInfo
     * @description 保存声音信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo) {
        // 鉴权
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        ThrowUtil.throwIf(albumInfo == null, ResultCodeEnum.DATA_ERROR, "专辑不存在");
        Long userId = AuthContextHolder.getUserId();
        ThrowUtil.throwIf(albumInfo.getUserId().equals(userId), ResultCodeEnum.PERMISSION);
        TrackInfo trackInfo = TrackInfo.VoToEntity(trackInfoVo);
        // 设置用户id
        trackInfo.setUserId(userId);
        ThrowUtil.throwIf(albumInfo == null, ResultCodeEnum.DATA_ERROR, "专辑不存在");
        // 1. 设置声音排序
        trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
        // 2. 状态审核中
        // 审核标题、简介
        ThrowUtil.throwIf(vodManager.checkTextContent(trackInfo.getTrackTitle()), ResultCodeEnum.SERVICE_ERROR,
                "标题含有违规内容");
        if (!vodManager.checkTextContent(trackInfo.getTrackIntro())) {
            trackInfo.setTrackIntro(null);
        }
        // 创建音频审核任务
        String taskId = vodManager.createVideoReviewTack(trackInfo.getMediaFileId());
        ThrowUtil.throwIf(StrUtil.isBlank(taskId), ResultCodeEnum.SERVICE_ERROR, "创建审核任务失败");
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_IN_PASS);
        trackInfo.setReviewTaskId(taskId);
        // 3. 设置音频格式、时长、大小
        String[] mediaFileIds = new String[] { trackInfo.getMediaFileId() };
        DescribeMediaInfosResponse mediaInfosResponse = vodManager.getFileOriginalInfo(mediaFileIds);
        String fileType = mediaInfosResponse.getMediaInfoSet()[0].getBasicInfo().getType();
        Long size = mediaInfosResponse.getMediaInfoSet()[0].getMetaData().getSize();
        Float audioDuration = mediaInfosResponse.getMediaInfoSet()[0].getMetaData().getAudioDuration();
        trackInfo.setMediaType(fileType);
        trackInfo.setMediaSize(size);
        trackInfo.setMediaDuration(BigDecimal.valueOf(audioDuration));
        // 4. 设置声音封面/数量
        if (StrUtil.isBlank(trackInfo.getCoverUrl())) {
            trackInfo.setCoverUrl(albumInfo.getCoverUrl());
        }
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        albumInfoMapper.updateById(albumInfo);
        ThrowUtil.throwIf(!this.save(trackInfo), ResultCodeEnum.SERVICE_ERROR, "保存声音信息失败");
        // 新增统计信息
        List<TrackStat> trackStatList = new ArrayList<>();
        for (TrackStatType trackStatType : TrackStatType.values()) {
            TrackStat trackStat = new TrackStat();
            trackStat.setTrackId(trackInfo.getId());
            trackStat.setStatType(trackStatType.getCode());
            trackStatList.add(trackStat);
        }
        trackStatMapper.insertBatch(trackStatList);
    }

    /**
     * @param @param         page
     * @param limit
     * @param trackInfoQuery
     * @return @return {@code Page<TrackListVo> }
     * @name findUserTrackPage
     * @description 查询用户声音分页列表
     */
    @Override
    public Page<TrackListVo> findUserTrackPage(Long page, Long limit, TrackInfoQuery trackInfoQuery) {
        // 1. 创建查询条件
        trackInfoQuery.setUserId(AuthContextHolder.getUserId());
        List<TrackStatType> trackStatTypes = Arrays.asList(TrackStatType.values());
        Page<TrackListVo> trackListVoPage = new Page<>(page, limit);
        return trackInfoMapper.findUserTrackPage(trackListVoPage, trackInfoQuery, trackStatTypes);
    }

    /**
     * @param @param      id
     * @param trackInfoVo
     * @return
     * @name updateTrackInfo
     * @description 更新声音信息
     */
    @Override
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        ThrowUtil.throwIf(!this.getById(id).getUserId().equals(AuthContextHolder.getUserId()),
                ResultCodeEnum.PERMISSION);
        TrackInfo trackInfo = TrackInfo.VoToEntity(trackInfoVo);
        ThrowUtil.throwIf(!this.updateById(trackInfo), ResultCodeEnum.SERVICE_ERROR, "更新声音信息失败");
    }

    /**
     * @param @param id
     * @return
     * @name deleteTrackInfo
     * @description 删除声音信息
     */
    @Override
    public void deleteTrackInfo(Long id) {
        ThrowUtil.throwIf(!this.getById(id).getUserId().equals(AuthContextHolder.getUserId()),
                ResultCodeEnum.PERMISSION);
        ThrowUtil.throwIf(!this.removeById(id), ResultCodeEnum.SERVICE_ERROR, "删除声音信息失败");
    }

    /**
     * @param @param id
     * @return @return {@code TrackInfoVo }
     * @name getTrackInfo
     * @description 查询声音信息
     */
    @Override
    public TrackInfoVo getTrackInfo(Long id) {
        // ThrowUtil.throwIf(!this.getById(id).getUserId().equals(AuthContextHolder.getUserId()),
        // ResultCodeEnum.PERMISSION);
        TrackInfo trackInfo = this.getById(id);
        TrackInfoVo trackInfoVo = new TrackInfoVo();
        BeanUtil.copyProperties(trackInfo, trackInfoVo);
        trackInfoVo.setId(id);
        return trackInfoVo;
    }

    /**
     * 查询专辑声音分页列表
     *
     * @param albumId 专辑ID
     * @param page    页码
     * @param limit   每页条数
     * @return 分页结果
     */
    @Override
    public Page<AlbumTrackListVo> findAlbumTrackPage(Long userId, Long albumId, Long page, Long limit) {
        // 1. 参数校验
        ThrowUtil.throwIf(albumId == null, "专辑ID不能为空");
        // 2. 查询专辑信息，获取付费类型和免费试听集数
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        ThrowUtil.throwIf(albumInfo == null, ResultCodeEnum.DATA_ERROR, "专辑不存在");
        // 3. 查询声音分页列表
        List<TrackStatType> trackStatTypes = Arrays.asList(TrackStatType.values());
        Page<AlbumTrackListVo> albumTrackListVoPage = new Page<>(page, limit);
        Page<AlbumTrackListVo> resultPage = trackInfoMapper.findAlbumTrackPage(albumTrackListVoPage, albumId,
                trackStatTypes);
        // 4. 处理付费标识
        List<AlbumTrackListVo> records = resultPage.getRecords();
        if (records.isEmpty()) {
            return resultPage;
        }
        // 5. 判断是否需要显示付费标识
        String payType = albumInfo.getPayType();
        boolean isFreeAlbum = "0101".equals(payType); // 0101-免费
        Integer tracksForFree = albumInfo.getTracksForFree() != null ? albumInfo.getTracksForFree() : 0;
        if (!isFreeAlbum) {
            // 非免费专辑，需要查询用户付费情况
            // 6. 收集需要检查的声音ID列表
            List<Long> needCheckTrackIds = records.stream()
                    .map(AlbumTrackListVo::getTrackId)
                    .toList();
            // 7. 调用用户服务查询付费情况（只有登录用户才查询）
            Map<Long, Integer> paidStateMap = null;
            if (userId != null) {
                try {
                    var result = userFeignClient.userIsPaidTrack(userId, albumId, needCheckTrackIds);
                    if (result != null && result.getData() != null) {
                        paidStateMap = result.getData();
                    }
                } catch (Exception e) {
                    log.error("查询用户付费情况失败，默认为未购买 - userId: {}, albumId: {}", userId, albumId, e);
                }
            }
            // 8. 设置付费标识
            Map<Long, Integer> finalPaidStateMap = paidStateMap;
            for (int i = 0; i < records.size(); i++) {
                AlbumTrackListVo trackVo = records.get(i);
                int orderNum = trackVo.getOrderNum();
                // 判断是否在免费试听范围内
                boolean isInFreeRange = orderNum <= tracksForFree;
                // 如果在免费范围内，不显示付费标识
                if (isInFreeRange) {
                    trackVo.setIsShowPaidMark(false);
                } else {
                    // 不在免费范围内，需要检查是否已购买
                    boolean isPaid = finalPaidStateMap != null
                            && finalPaidStateMap.get(trackVo.getTrackId()) != null
                            && finalPaidStateMap.get(trackVo.getTrackId()) == 1;
                    // 如果未购买，显示付费标识
                    trackVo.setIsShowPaidMark(!isPaid);
                }
            }
        } else {
            // 免费专辑，所有声音都不显示付费标识
            records.forEach(trackVo -> trackVo.setIsShowPaidMark(false));
        }
        log.info("查询专辑声音列表 - albumId: {}, page: {}, limit: {}, total: {}",
                albumId, page, limit, resultPage.getTotal());
        return resultPage;
    }

    /**
     * 获取声音统计信息
     *
     * @param trackId 声音ID
     * @return 声音统计信息
     */
    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        // 直接从track_stat表查询声音统计信息
        List<TrackStatType> trackStatTypes = Arrays.asList(TrackStatType.values());
        TrackStatVo trackStatVo = trackStatMapper.getTrackStatVoByTrackId(trackId, trackStatTypes);
        // 如果没有统计数据，返回默认值
        if (trackStatVo == null) {
            trackStatVo = new TrackStatVo();
            trackStatVo.setPlayStatNum(0);
            trackStatVo.setCollectStatNum(0);
            trackStatVo.setPraiseStatNum(0);
            trackStatVo.setCommentStatNum(0);
        }
        return trackStatVo;
    }

    /**
     * 获取用户声音分集购买支付列表
     *
     * @param userId  用户ID
     * @param trackId 声音ID
     * @return 支付选项列表
     */
    @Override
    public List<Map<String, Object>> findUserTrackPaidList(Long userId, Long trackId) {
        log.info("获取用户声音分集购买支付列表 - userId: {}, trackId: {}", userId, trackId);
        // 1. 查询当前声音信息
        TrackInfo currentTrack = this.getById(trackId);
        ThrowUtil.throwIf(currentTrack == null, "声音不存在");
        // 2. 查询专辑信息
        AlbumInfo albumInfo = albumInfoMapper.selectById(currentTrack.getAlbumId());
        ThrowUtil.throwIf(albumInfo == null, "专辑不存在");
        // 3. 查询该声音之后的所有声音（包括当前声音）
        LambdaQueryWrapper<TrackInfo> afterQueryWrapper = new LambdaQueryWrapper<>();
        afterQueryWrapper.eq(TrackInfo::getAlbumId, currentTrack.getAlbumId())
                .ge(TrackInfo::getOrderNum, currentTrack.getOrderNum())
                .orderByAsc(TrackInfo::getOrderNum);
        List<TrackInfo> afterTracks = this.list(afterQueryWrapper);
        log.info("当前声音及之后的声音总数: {}", afterTracks.size());
        // 4. 查询专辑下所有声音（用于计算购买全集）
        LambdaQueryWrapper<TrackInfo> allQueryWrapper = new LambdaQueryWrapper<>();
        allQueryWrapper.eq(TrackInfo::getAlbumId, currentTrack.getAlbumId())
                .orderByAsc(TrackInfo::getOrderNum);
        List<TrackInfo> allTracks = this.list(allQueryWrapper);
        List<Long> allTrackIds = allTracks.stream().map(TrackInfo::getId).toList();
        log.info("专辑下所有声音总数: {}", allTracks.size());
        // 5. 查询用户已购买的声音ID列表
        Map<Long, Integer> userPaidMap = new HashMap<>();
        if (!allTrackIds.isEmpty()) {
            var result = userFeignClient.userIsPaidTrack(userId, currentTrack.getAlbumId(), allTrackIds);
            if (result != null && result.getData() != null) {
                userPaidMap = result.getData();
            }
        }
        // 6. 过滤掉已购买的声音，得到当前声音之后的未购买列表
        List<TrackInfo> unpaidAfterTracks = new ArrayList<>();
        for (TrackInfo track : afterTracks) {
            if (userPaidMap.get(track.getId()) == null || userPaidMap.get(track.getId()) == 0) {
                unpaidAfterTracks.add(track);
            }
        }
        // 7. 过滤掉已购买的声音，得到专辑下所有未购买的声音列表
        List<TrackInfo> unpaidAllTracks = new ArrayList<>();
        for (TrackInfo track : allTracks) {
            if (userPaidMap.get(track.getId()) == null || userPaidMap.get(track.getId()) == 0) {
                unpaidAllTracks.add(track);
            }
        }
        log.info("当前声音之后未购买的声音数量: {}, 专辑下所有未购买的声音数量: {}",
                unpaidAfterTracks.size(), unpaidAllTracks.size());
        // 8. 构建返回结果
        List<Map<String, Object>> result = new ArrayList<>();
        // 8.1 购买单集（当前声音）
        if (!unpaidAfterTracks.isEmpty()) {
            Map<String, Object> singleOption = new HashMap<>();
            singleOption.put("name", "购买单集");
            singleOption.put("price", albumInfo.getPrice());
            singleOption.put("trackCount", 1);
            result.add(singleOption);
        }

        // 8.2 购买后10集、后20集、后30集......（使用循环生成）
        if (unpaidAfterTracks.size() > 1) {
            int unpaidCount = unpaidAfterTracks.size();
            int step = 10; // 每次增加10集

            for (int i = step; i <= unpaidCount; i += step) {
                int count = i;
                BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(count));

                Map<String, Object> option = new HashMap<>();
                option.put("name", "购买后" + count + "集");
                option.put("price", price);
                option.put("trackCount", count);
                result.add(option);
            }

            // 如果未购买数量不是10的倍数，添加最后一个选项（购买全部剩余）
            if (unpaidCount % step != 0) {
                BigDecimal priceAll = albumInfo.getPrice().multiply(new BigDecimal(unpaidCount));
                Map<String, Object> lastOption = new HashMap<>();
                lastOption.put("name", "购买后" + unpaidCount + "集");
                lastOption.put("price", priceAll);
                lastOption.put("trackCount", unpaidCount);
                result.add(lastOption);
            }
        }

        log.info("购买选项数量: {}", result.size());
        return result;
    }

    /**
     * 根据声音ID列表查询专辑ID映射
     *
     * @param trackIdList 声音ID列表
     * @return Map<声音ID, 专辑ID>
     */
    @Override
    public Map<Long, Long> getAlbumIdsByTrackIds(List<Long> trackIdList) {
        ThrowUtil.throwIf(trackIdList == null || trackIdList.isEmpty(), "声音ID列表不能为空");
        log.info("根据声音ID列表查询专辑ID - trackIdList size: {}", trackIdList.size());

        LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(TrackInfo::getId, trackIdList)
                .select(TrackInfo::getId, TrackInfo::getAlbumId);
        List<TrackInfo> trackInfoList = this.list(queryWrapper);

        // 返回Map<声音ID, 专辑ID>
        Map<Long, Long> trackAlbumMap = trackInfoList.stream()
                .collect(Collectors.toMap(TrackInfo::getId, TrackInfo::getAlbumId));

        log.info("查询到的声音-专辑映射数量: {}", trackAlbumMap.size());
        return trackAlbumMap;
    }
}
